// keel_ctdev.cpp : Defines the exported functions for the DLL application.
//

#include "stdafx.h"

#include <Mmsystem.h>
#include <map>
#include <vector>

#include "JClass.h"

#define _USE_MATH_DEFINES
#include <math.h>

#include "keel_runtime_ctrl_ControllerDeviceManager.h"
#include "backstage_runtime_ctrl_ControllerDeviceImpl.h"

struct JoyCap
{
	int bHasZ;
	int bHasR;
	int bHasU;
	int bHasV;
};

// jclass g_ControllerDeviceImpl;
// jmethodID g_ControllerDeviceImpl_init;
// jfieldID g_ControllerDeviceImpl_nativeContext;
// jclass g_String;

std::map<int,JoyCap> g_joyMap;

JNIEXPORT jobjectArray JNICALL Java_keel_runtime_ctrl_ControllerDeviceManager_scanControllerDevicesNative( JNIEnv *env, jobject )
{
// 	if(g_ControllerDeviceImpl==NULL)
// 	{
// 		jclass t=env->FindClass("backstage/runtime/ctrl/ControllerDeviceImpl");
// 		if(t==NULL)
// 			return NULL;
// 		g_ControllerDeviceImpl=reinterpret_cast<jclass>(env->NewGlobalRef(t));
// 	}
// 	if(g_ControllerDeviceImpl_init==NULL)
// 		g_ControllerDeviceImpl_init=env->GetMethodID(g_ControllerDeviceImpl, "<init>","(IIIIILjava/lang/String;[I[I[Ljava/lang/String;)V");
// 	if(g_ControllerDeviceImpl_nativeContext==NULL)
// 		g_ControllerDeviceImpl_nativeContext=env->GetFieldID(g_ControllerDeviceImpl,"nativeContext", "J");
// 	if(g_String==NULL)
// 		g_String=reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass("java/lang/String")));

	int cout=0;
	int n=joyGetNumDevs();
	JOYCAPS pjc;
	std::vector<jobject> devices;
	for(int i=0;i<n;++i)
	{
		if(joyGetDevCaps(i,&pjc,sizeof(pjc))==JOYERR_NOERROR)
		{
			++cout;
			JoyCap cap={0};
			if(pjc.wCaps & JOYCAPS_HASZ)
				cap.bHasZ=1;
			if(pjc.wCaps & JOYCAPS_HASR)
				cap.bHasR=1;
			if(pjc.wCaps & JOYCAPS_HASU)
				cap.bHasU=1;
			if(pjc.wCaps & JOYCAPS_HASV)
				cap.bHasV=1;
			g_joyMap[i]=cap;

			jstring axisNamesN[6]={
			 env->NewStringUTF("X axis"),
			 env->NewStringUTF("Y axis")};
		    int axes=2;
			if(cap.bHasZ)
				axisNamesN[axes++]=env->NewStringUTF("Z axis");
			if(cap.bHasR)
				axisNamesN[axes++]=env->NewStringUTF("R axis");
			if(cap.bHasU)
				axisNamesN[axes++]=env->NewStringUTF("U axis");
			if(cap.bHasV)
				axisNamesN[axes++]=env->NewStringUTF("V axis");

			jobjectArray axisNames=NULL;
			try
			{
				env->NewObjectArray(axes,JString::getClass(env),NULL);
			}catch(JNIPP::JException&)
			{
				continue;
			}
			
			jintArray axisMins=env->NewIntArray(axes);
			jint axisMinsN[6];
			jintArray axisMaxs=env->NewIntArray(axes);
			jint axisMaxsN[6];

			for(int j=0;j<axes;++j)
			{
				env->SetObjectArrayElement(axisNames,j,axisNamesN[j]);
				axisMinsN[j]=0;
				axisMaxsN[j]=65534;
			}
			env->SetIntArrayRegion(axisMins,0,axes,axisMinsN);
			env->SetIntArrayRegion(axisMaxs,0,axes,axisMaxsN);

			JControllerDeviceImpl device;
			try
			{
			device.create(env,(jint)pjc.wNumButtons,(jint)axes,(jint)((pjc.wCaps&JOYCAPS_HASPOV)?1:0),(jint)pjc.wMid,(jint)pjc.wPid,
				env->NewString(reinterpret_cast<jchar*>(pjc.szPname),_tcslen(pjc.szPname)),axisMins,axisMaxs,axisNames);
			device.setnativeContext(i);
			}catch(JNIPP::JException&)
			{
				continue;
			}
// 			jobject device=env->NewObject(g_ControllerDeviceImpl,g_ControllerDeviceImpl_init
// 				pjc.wNumButtons,axes,((pjc.wCaps&JOYCAPS_HASPOV)?1:0),pjc.wMid,pjc.wPid,
// 				env->NewString(reinterpret_cast<jchar*>(pjc.szPname),_tcslen(pjc.szPname)),axisMins,axisMaxs,axisNames);
//			env->SetLongField(device,g_ControllerDeviceImpl_nativeContext,i);
			devices.push_back(device);
		}
	}
	jobjectArray ret=NULL;
	try
	{
		env->NewObjectArray(cout,JControllerDeviceImpl::getClass(env),NULL);
	}catch(JNIPP::JException&)
	{
		return NULL;
	}
	for(int i=0;i<cout;++i)
		env->SetObjectArrayElement(ret,i,devices[i]);
	return ret;
}


// jclass g_ControllerDeviceImpl2;
// jfieldID g_ControllerDeviceImpl_nativeContext2;
// jfieldID g_ControllerDeviceImpl_buttonsPressed;
// jfieldID g_ControllerDeviceImpl_axisValues;
// jfieldID g_ControllerDeviceImpl_hatswitchValues;

JNIEXPORT void JNICALL Java_backstage_runtime_ctrl_ControllerDeviceImpl_poll( JNIEnv *env, jobject pthis)
{
// 	if(g_now_ControllerDeviceImpl==NULL)
// 	{
// 		jclass t=env->GetObjectClass(pthis);
// 		g_now_ControllerDeviceImpl=env->NewGlobalRef(t);
// 		env->DeleteLocalRef(t);
// 	}
// 	if(g_ControllerDeviceImpl2==NULL)
// 	{
// 		jclass t=env->FindClass("backstage/runtime/ctrl/ControllerDeviceImpl");
// 		if(t==NULL)
// 			return;
// 		g_ControllerDeviceImpl2=reinterpret_cast<jclass>(env->NewGlobalRef(t));
// 	}
// 	if(g_ControllerDeviceImpl_nativeContext2==NULL)
// 		g_ControllerDeviceImpl_nativeContext2=env->GetFieldID(g_ControllerDeviceImpl2,"nativeContext", "J");
// 	if(g_ControllerDeviceImpl_buttonsPressed==NULL)
// 		g_ControllerDeviceImpl_buttonsPressed=env->GetFieldID(g_ControllerDeviceImpl2,"buttonsPressed", "[Z");
// 	if(g_ControllerDeviceImpl_axisValues==NULL)
// 		g_ControllerDeviceImpl_axisValues=env->GetFieldID(g_ControllerDeviceImpl2,"axisValues", "[I");
// 	if(g_ControllerDeviceImpl_hatswitchValues==NULL)
// 		g_ControllerDeviceImpl_hatswitchValues=env->GetFieldID(g_ControllerDeviceImpl2,"hatswitchValues", "[F");
	JControllerDeviceImpl device(env,pthis);

	int id=0;
	jbooleanArray buttonsPressed=NULL;
	jintArray axisValues=NULL;
	jfloatArray hatswitchValues=NULL;

	try
	{
		id=device.getnativeContext();
		buttonsPressed=reinterpret_cast<jbooleanArray>(device.getbuttonsPressed());
		axisValues=reinterpret_cast<jintArray>(device.getaxisValues());
		hatswitchValues=reinterpret_cast<jfloatArray>(device.gethatswitchValues());
	}catch(JNIPP::JException&)
	{
		return;
	}

// 	int id=env->GetLongField(pthis,g_ControllerDeviceImpl_nativeContext2);
// 	jbooleanArray buttonsPressed=reinterpret_cast<jbooleanArray>(env->GetObjectField(pthis,g_ControllerDeviceImpl_buttonsPressed));
// 	jintArray axisValues=reinterpret_cast<jintArray>(env->GetObjectField(pthis,g_ControllerDeviceImpl_axisValues));
// 	jfloatArray hatswitchValues=reinterpret_cast<jfloatArray>(env->GetObjectField(pthis,g_ControllerDeviceImpl_hatswitchValues));

	JOYINFOEX pji={0};
	pji.dwSize=sizeof(pji);
	pji.dwFlags=JOY_RETURNALL|JOY_USEDEADZONE;
	joyGetPosEx(id,&pji);

	jboolean buttonsPressedN[32];
	int buttonsPressedLen=env->GetArrayLength(buttonsPressed);
	if(buttonsPressedLen>0)
	{
		for(int i=0;i<buttonsPressedLen;++i)
			buttonsPressedN[i]=(pji.dwButtons&(1<<i))?1:0;
		env->SetBooleanArrayRegion(buttonsPressed,0,buttonsPressedLen,buttonsPressedN);
	}
	
	if(env->GetArrayLength(axisValues)>0)
	{
		JoyCap cap=g_joyMap[id];
		jint axisValuesN[6]={pji.dwXpos,pji.dwYpos};
		int axes=2;
		if(cap.bHasZ)
			axisValuesN[axes++]=pji.dwZpos;
		if(cap.bHasR)
			axisValuesN[axes++]=pji.dwRpos;
		if(cap.bHasU)
			axisValuesN[axes++]=pji.dwUpos;
		if(cap.bHasV)
			axisValuesN[axes++]=pji.dwVpos;

		for(int i=0;i<axes;++i)
		{
			if(axisValuesN[i]>=65535)
				axisValuesN[i]=65534;
			if(axisValuesN[i]<0)
				axisValuesN[i]=0;
		}
		env->SetIntArrayRegion(axisValues,0,axes,axisValuesN);
	}
	
	int hatswitchValuesLen=env->GetArrayLength(hatswitchValues);
	if(hatswitchValuesLen>0)
	{
		jfloat t;
		if(pji.dwPOV==JOY_POVCENTERED)
			t=-1.0;
		else
			t=pji.dwPOV*M_PI/18000.0;
		env->SetFloatArrayRegion(hatswitchValues,0,1,&t);
	}
}

